﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Base;
using UtilZ.Dotnet.Ex.Exceptions;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Base
{
    /// <summary>
    /// tcp客户端
    /// </summary>
    public class NetTcpClient2 : ClientAbs
    {
        private readonly TcpClientInitPara _tcpClientInitPara;

        private NetTcpChannel _tcpChannel = null;
        /// <summary>
        /// 当前已建立的网络传输对象
        /// </summary>
        public NetTcpChannel TcpChannel
        {
            get { return this._tcpChannel; }
        }


        private readonly AutoResetEvent _reconnectCompletedEventHandler = new AutoResetEvent(false);
        private readonly IThreadEx _reconnetThread = null;
        private event EventHandler<NetConnectedChangedArgs> _connectChanged;
        /// <summary>
        /// 连接状态改变通知事件
        /// </summary>
        public event EventHandler<NetConnectedChangedArgs> ConnectChanged
        {
            add
            {
                _connectChanged += value;
                var tcpChannel = this._tcpChannel;
                if (tcpChannel != null)
                {
                    this.OnRaiseConnectChanged(tcpChannel.Client);
                }
            }
            remove { _connectChanged -= value; }
        }

        private readonly object _connectedLock = new object();
        private bool _connected = false;
        /// <summary>
        /// 获取socket连接状态
        /// </summary>
        public bool Connected
        {
            get { return _connected; }
        }

        /// <summary>
        /// 外部断开连接标识[true:连接断开;false:连接正常]
        /// </summary>
        private bool _disconnectFlag = false;

        /// <summary>
        /// 触发连接状态改变
        /// </summary>
        /// <param name="client">Socket</param>
        protected void OnRaiseConnectChanged(Socket client)
        {
            var handler = this._connectChanged;
            if (handler != null)
            {
                var remoteEndPoint = this._tcpClientInitPara.RemoteEndPoint;
                NetConnectedChangedArgs args;
                try
                {
                    if (client != null)
                    {
                        args = new NetConnectedChangedArgs(client.Connected ? ConnectionStatus.Opened : ConnectionStatus.Closed, client.LocalEndPoint, remoteEndPoint);
                    }
                    else
                    {
                        args = new NetConnectedChangedArgs(ConnectionStatus.Closed, null, remoteEndPoint);
                    }
                }
                catch (ObjectDisposedException)
                {
                    args = new NetConnectedChangedArgs(ConnectionStatus.Closed, null, remoteEndPoint);
                }
                catch (Exception ex)
                {
                    ZLoger.Error(ex);
                    args = new NetConnectedChangedArgs(ConnectionStatus.Closed, null, remoteEndPoint);
                }

                handler(this, args);
                this.OnConnectChanged(args);
            }
        }

        /// <summary>
        /// 触发连接状态改变
        /// </summary>
        /// <param name="args">NetConnectedChangedArgs</param>
        protected virtual void OnConnectChanged(NetConnectedChangedArgs args)
        {

        }

        /// <summary>
        /// 最后一次接收数据时间
        /// </summary>
        protected long _lastReceiveDataTimestamp = 0;


        /// <summary>
        /// 
        /// </summary>
        protected readonly string _remoteIpStr;


        /// <summary>
        /// Socket
        /// </summary>
        public Socket Socket
        {
            get
            {
                var netTcpChannel = this._tcpChannel;
                if (netTcpChannel == null)
                {
                    return null;
                }

                return netTcpChannel.Client;
            }
        }


        private readonly object _lock = new object();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="initPara">Tcp客户端初始化参数</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public NetTcpClient2(TcpClientInitPara initPara)
            : base(initPara)
        {
            this._tcpClientInitPara = initPara;
            this._remoteIpStr = NetHelper.GetIPString(initPara.RemoteEndPoint);
            this._lastReceiveDataTimestamp = TimeEx.GetTimestamp();

            if (initPara.ConnectType == ConnectTypes.Active)
            {
                this._reconnetThread = new ThreadEx(this.ReconnetThreadMethod, "运维客户端连接线程", true);
            }
            else
            {
                this.SetNetTcpChannel(initPara.Client);
            }
        }



        private void ReconnetThreadMethod(ThreadExPara threadPara)
        {
            try
            {
                var initPara = (TcpClientInitPara)base._initPara;
#if NET4_0
                var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
#else
                var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
#endif
                bool connectResult;
                int reconnectIntervalMilliseconds = initPara.ReconnectIntervalMilliseconds;

                while (!threadPara.Token.IsCancellationRequested)
                {
                    try
                    {
                        connectResult = this.PrimitiveConect(socket);
                        try
                        {
                            this._reconnectCompletedEventHandler.Set();
                        }
                        catch (ObjectDisposedException)
                        {
                            break;
                        }

                        if (!connectResult)
                        {
                            socket.Dispose();
                        }
                        break;
                    }
                    catch (ObjectDisposedException)
                    {
                        break;
                    }
                    catch (SocketException sex) when (NetHelper.SocketErrorIsConnectFail(sex.SocketErrorCode))
                    {
                        //由于目标计算机积极拒绝，无法连接
                        if (reconnectIntervalMilliseconds > 0)
                        {
                            ZLoger.Warn(sex, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                            Thread.Sleep(reconnectIntervalMilliseconds);
                        }
                        else
                        {
                            ZLoger.Warn(sex, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,立即重试...");
                        }
                    }
                    catch (Exception exi)
                    {
                        if (reconnectIntervalMilliseconds > 0)
                        {
                            ZLoger.Warn(exi, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                            Thread.Sleep(reconnectIntervalMilliseconds);
                        }
                        else
                        {
                            ZLoger.Warn(exi, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,立即重试...");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }


        private bool PrimitiveConect(Socket socket)
        {
            if (this._connected)
            {
                return false;
            }

            lock (this._connectedLock)
            {
                if (this._connected)
                {
                    return false;
                }

                socket.Connect(this._tcpClientInitPara.RemoteEndPoint);
                try
                {
                    this.SetNetTcpChannel(socket);
                    return true;
                }
                catch (Exception ex)
                {
                    ZLoger.Error(ex);
                    return false;
                }
            }
        }

        private void SetNetTcpChannel(Socket socket)
        {
            string receiveThreadName = this._tcpClientInitPara.ReceiveThreadName;
            if (string.IsNullOrWhiteSpace(receiveThreadName))
            {
                receiveThreadName = $"tcp接收数据线程{NetHelper.GetIPPortString(socket.RemoteEndPoint)}";
            }

            if (this._tcpClientInitPara.KeepAlive)
            {
#if CORE
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, this._tcpClientInitPara.KeepAliveTimeSeconds);
                socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, this._tcpClientInitPara.KeepAliveIntervalSeconds);
                socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, this._tcpClientInitPara.KeepRetryCount);
#else
                int size = System.Runtime.InteropServices.Marshal.SizeOf((uint)0);
                byte[] keepaliveOptionsValues = new byte[size * 3];
                int offset = 0;

                uint keepAliveEnable = 1;
                uint keepAliveTimeMilliseconds = (uint)this._tcpClientInitPara.KeepAliveTimeSeconds * 1000;
                uint keepAliveIntervalMilliseconds = (uint)this._tcpClientInitPara.KeepAliveIntervalSeconds * 1000;

                BitConverter.GetBytes(keepAliveEnable).CopyTo(keepaliveOptionsValues, offset);
                offset += size;

                BitConverter.GetBytes(keepAliveTimeMilliseconds).CopyTo(keepaliveOptionsValues, offset);
                offset += size;

                BitConverter.GetBytes(keepAliveIntervalMilliseconds).CopyTo(keepaliveOptionsValues, offset);

                socket.IOControl(IOControlCode.KeepAliveValues, keepaliveOptionsValues, null);
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
#endif
            }

            this._tcpChannel = new NetTcpChannel(this, receiveThreadName, socket, this._tcpClientInitPara.BufferSize, this._tcpClientInitPara.Unpacker, this.DisconnectNotify, this.DefaultReceiveDataNotify, this._tcpClientInitPara.Certificate);
            this._connected = true;
            this.OnRaiseConnectChanged(this._tcpChannel.Client);
        }

        private void DefaultReceiveDataNotify(NetTcpChannel netTcpChannel, int len)
        {
            this._lastReceiveDataTimestamp = TimeEx.GetTimestamp();
        }

        private void DisconnectNotify(NetTcpChannel tcpChannel)
        {
            try
            {
                lock (this._connectedLock)
                {
                    this._connected = false;
                    if (tcpChannel != null)
                    {
                        this.OnRaiseConnectChanged(tcpChannel.Client);
                        tcpChannel.Dispose();
                        if (tcpChannel == this._tcpChannel)
                        {
                            this._tcpChannel = null;
                        }
                    }

                    if (this._tcpClientInitPara.ConnectType == ConnectTypes.Active &&
                        !this._disconnectFlag &&
                        !this._disposed)
                    {
                        this.ConnectAsync();
                    }
                }
            }
            catch (ObjectDisposedException)
            { }
            catch (Exception ex)
            {
                ZLoger.Warn(ex);
            }
        }

        /// <summary>
        /// 同步连接
        /// </summary>
        public void Connect()
        {
            this._disconnectFlag = false;
#if NET4_0
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
#else
            var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
#endif
            try
            {
                var localEndPoint = this._tcpClientInitPara.LocalEndPoint;
                if (localEndPoint != null)
                {
                    socket.Bind(localEndPoint);
                }

                if (!this.PrimitiveConect(socket))
                {
                    socket.Dispose();
                }
            }
            catch
            {
                socket.Dispose();
                throw;
            }
        }

        /// <summary>
        /// 异步连接
        /// </summary>
        public void ConnectAsync()
        {
            this._disconnectFlag = false;
            this._reconnetThread.Start();
        }




        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="sendDataOptions">发送的数据选项</param>
        protected override void PrimitiveSend(ISendDataOptions sendDataOptions)
        {
            if (sendDataOptions == null)
            {
                return;
            }

            var tcpSendOptions = (TcpSendDataOptions)sendDataOptions;
            BufferData data = tcpSendOptions.Data;
            if (data == null)
            {
                return;
            }

            this.Send(data, tcpSendOptions.SocketFlags);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bufferData">数据</param>
        /// <param name="socketFlags">SocketFlags</param>
        /// <param name="millisecondsTimeout">发送超时时长,默认10000,单位:毫秒 </param>
        public void Send(BufferData bufferData, SocketFlags socketFlags, int millisecondsTimeout = 10000)
        {
            Monitor.Enter(this._lock);
            try
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException(this.ToString());
                }

                if (this._tcpChannel == null)
                {
                    try
                    {
                        this.Connect();
                    }
                    catch (Exception ex)
                    {
                        throw new ConnectException("未连接到服务端" + ex.Message);
                    }
                }

                NetTcpChannel netTcpChannel = this._tcpChannel;
                if (netTcpChannel == null)
                {
                    throw new ConnectException("未连接到服务端");
                }

                switch (bufferData.DataType)
                {
                    case BufferDataType.Bytes:
                        this.Send(bufferData.Bytes, (int)bufferData.Offset, (int)bufferData.Length, socketFlags, millisecondsTimeout);
                        break;
                    case BufferDataType.Stream:
                        netTcpChannel.Send(bufferData.Stream, bufferData.Offset, bufferData.Length, socketFlags);
                        break;
                    case BufferDataType.File:
                        if (bufferData.Offset <= 0 && bufferData.Length < 0)
                        {
                            netTcpChannel.Send(bufferData.FilePath);
                        }
                        else
                        {
                            using (var fs = File.OpenRead(bufferData.FilePath))
                            {
                                bufferData.UpdateFileBufferLength(fs.Length);
                                netTcpChannel.Send(fs, bufferData.Offset, bufferData.Length, socketFlags);
                            }
                        }
                        break;
#if CORE
                    case BufferDataType.Memory:
                        this._tcpChannel.Send(bufferData.MemoryBuffer);
                        break;
#endif
                    default:
                        throw new NotImplementedException($"未实现的缓存数据类型{bufferData.DataType.ToString()}");
                }
            }
            finally
            {
                Monitor.Exit(this._lock);
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        /// <param name="offset">数据中偏移位置</param>
        /// <param name="size">要发送的数据大小</param>
        /// <param name="socketFlags">SocketFlags</param>
        /// <param name="millisecondsTimeout">发送超时时长,默认10000,单位:毫秒 </param>
        public void Send(byte[] buffer, int offset, int size, SocketFlags socketFlags, int millisecondsTimeout = 10000)
        {
            Monitor.Enter(this._lock);
            try
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException(this.ToString());
                }

                if (this._tcpChannel == null)
                {
                    try
                    {
                        this.Connect();
                    }
                    catch (Exception ex)
                    {
                        throw new ConnectException("未连接到服务端" + ex.Message);
                    }
                }

                NetTcpChannel netTcpChannel;
                while (true)
                {
                    try
                    {
                        netTcpChannel = this._tcpChannel;
                        if (netTcpChannel == null)
                        {
                            throw new ConnectException("未连接到服务端");
                        }

                        netTcpChannel.Send(buffer, offset, size, socketFlags);
                        break;
                    }
                    catch (NullReferenceException)
                    {
                        throw new ConnectException("未连接到服务端");
                    }
                    catch (SocketException sex) when (NetHelper.SocketErrorIsSendFail(sex.SocketErrorCode))
                    {
                        //	"远程主机强迫关闭了一个现有的连接。"
                        ZLoger.Warn(sex);
                        this.DisconnectNotify(this._tcpChannel);
                        if (this._tcpClientInitPara.ConnectType != ConnectTypes.Active)
                        {
                            throw;
                        }

                        if (!this.SendReconect(millisecondsTimeout))
                        {
                            break;
                        }

                        continue;
                    }
                }
            }
            finally
            {
                Monitor.Exit(this._lock);
            }
        }

        /// <summary>
        /// 发送数据方法
        /// </summary>
        /// <param name="buffer">要发送的数据</param>
        /// <param name="millisecondsTimeout">发送超时时长,默认10000,单位:毫秒 </param>
        public void Send(byte[] buffer, int millisecondsTimeout = 10000)
        {
            Monitor.Enter(this._lock);
            try
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException(this.ToString());
                }

                if (this._tcpChannel == null)
                {
                    try
                    {
                        this.Connect();
                    }
                    catch (Exception ex)
                    {
                        throw new ConnectException("未连接到服务端" + ex.Message);
                    }
                }

                NetTcpChannel netTcpChannel;
                while (true)
                {
                    try
                    {
                        netTcpChannel = this._tcpChannel;
                        if (netTcpChannel == null)
                        {
                            throw new ConnectException("未连接到服务端");
                        }

                        netTcpChannel.Send(buffer);
                        break;
                    }
                    catch (NullReferenceException)
                    {
                        throw new ConnectException("未连接到服务端");
                    }
                    catch (SocketException sex) when (NetHelper.SocketErrorIsSendFail(sex.SocketErrorCode))
                    {
                        //	"远程主机强迫关闭了一个现有的连接。"
                        ZLoger.Warn(sex);
                        this.DisconnectNotify(this._tcpChannel);
                        if (this._tcpClientInitPara.ConnectType != ConnectTypes.Active)
                        {
                            throw;
                        }

                        if (!this.SendReconect(millisecondsTimeout))
                        {
                            break;
                        }

                        continue;
                    }
                }
            }
            finally
            {
                Monitor.Exit(this._lock);
            }
        }

        private bool SendReconect(int millisecondsTimeout)
        {
            var watch = Stopwatch.StartNew();
            try
            {
                var reconnectIntervalMilliseconds = this._tcpClientInitPara.ReconnectIntervalMilliseconds;
                ZLoger.Info($"未连接到服务端,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                while (!this._reconnectCompletedEventHandler.WaitOne(reconnectIntervalMilliseconds))
                {
                    if (watch.Elapsed.TotalMilliseconds - millisecondsTimeout > 0.00001)
                    {
                        throw new ConnectException($"因重失败发送超时");
                    }
                }
                return true;
            }
            catch (ObjectDisposedException)
            {
                return false;
            }
            finally
            {
                watch.Stop();
            }
        }





        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            this._disconnectFlag = true;
            if (this._reconnetThread != null)
            {
                this._reconnetThread.Stop();
            }

            if (this._tcpChannel != null)
            {
                this._tcpChannel.Dispose();
            }
        }



        /// <summary>
        /// 检查连接是否超时[超时返回true,否则返回false]
        /// </summary>
        /// <param name="millisecondsTimeout">超时毫秒值</param>
        /// <returns>超时返回true,否则返回false</returns>
        public virtual bool CheckConnectTimeout(double millisecondsTimeout)
        {
            lock (this._disposeLock)
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException(this.ToString());
                }

                if (this._tcpChannel == null)
                {
                    this.OnRaiseConnectChanged(null);
                    return false;
                }

                var offset = TimeEx.GetTimestamp() - this._lastReceiveDataTimestamp;
                const double PRE = 0.001d;
                if (offset - millisecondsTimeout > PRE)
                {
                    ZLoger.Debug($"检查线程检测到{this._remoteIpStr}设备离线超时,强制断开该连接");
                    this.OnRaiseConnectChanged(this._tcpChannel.Client);
                    return false;
                }

                return true;
            }
        }



        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            lock (this._disposeLock)
            {
                try
                {
                    if (this._reconnetThread != null)
                    {
                        this._reconnetThread.Stop();
                        this._reconnetThread.Dispose();
                    }

                    try
                    {
                        this._reconnectCompletedEventHandler.Dispose();
                    }
                    catch (ObjectDisposedException)
                    { }

                    if (this._tcpChannel != null)
                    {
                        this._tcpChannel.Dispose();
                    }

                    //ZLoger.Warn("NetTcpClient2.Dispose");
                }
                catch (Exception ex)
                {
                    ZLoger.Error(ex, "资源释放异常");
                }
            }
        }
    }

    /// <summary>
    /// tcp传输对象
    /// </summary>
    public class NetTcpChannel : IDisposable
    {
        private readonly NetTcpClient2 _owner;

        private readonly Socket _client;
        /// <summary>
        /// 
        /// </summary>
        public Socket Client
        {
            get { return _client; }
        }

        private readonly X509Certificate _serverCertificate;
        private readonly int _bufferSize;
        private readonly ThreadEx _socketRevDataThread;
        private readonly Action<NetTcpChannel> _disconnectNotify;
        private readonly Action<NetTcpChannel, int> _receiveDataNotify;

        private readonly IUnpacker _unpacker;
        /// <summary>
        /// 网络传输数据分包器器
        /// </summary>
        public IUnpacker Unpacker
        {
            get { return this._unpacker; }
        }

        private readonly NetLinkInfo _linkInfo;
        /// <summary>
        /// 获取连接终节点信息
        /// </summary>
        public NetLinkInfo LinkInfo
        {
            get { return _linkInfo; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="owner">TCP通道当前所属NetTcpClient</param>
        /// <param name="receiveThreadName">接收线程名称</param>
        /// <param name="client">Socket</param>
        /// <param name="bufferSize">待拆分的缓冲数据大小</param>
        /// <param name="unpacker"></param>
        /// <param name="disconnectNotify">连接断开通知</param>
        /// <param name="receiveDataNotify">接收到数据通知</param>
        /// <param name="serverCertificate">ssl安全证书,无证书安全填null</param>
        public NetTcpChannel(NetTcpClient2 owner, string receiveThreadName, Socket client, int bufferSize,
            IUnpacker unpacker, Action<NetTcpChannel> disconnectNotify, Action<NetTcpChannel, int> receiveDataNotify,
            X509Certificate serverCertificate)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            //int mtu = NetHelper.MTU_MAX * 2;//MTU最大值,原本是(65507=65535-20-8),保留一点,靠谱点
            //if (bufferSize < mtu)
            //{
            //    throw new ArgumentOutOfRangeException(nameof(bufferSize), $"缓存不能小于{mtu},值{bufferSize}无效");
            //}

            if (unpacker == null)
            {
                throw new ArgumentNullException(nameof(unpacker));
            }

            if (disconnectNotify == null)
            {
                throw new ArgumentNullException(nameof(disconnectNotify));
            }

            this._owner = owner;
            this._client = client;
            this._bufferSize = bufferSize;
            this._disconnectNotify = disconnectNotify;

            if (receiveDataNotify == null)
            {
                this._receiveDataNotify = this.DefaultReceiveDataNotify;
            }
            else
            {
                this._receiveDataNotify = receiveDataNotify;
            }
            this._unpacker = unpacker;
            this._linkInfo = new NetLinkInfo(client.RemoteEndPoint, client.LocalEndPoint);
            this._serverCertificate = serverCertificate;

            this._socketRevDataThread = new ThreadEx(this.RevDataThreadMethod, receiveThreadName, true);
            this._socketRevDataThread.Start(this._client);
        }

        private bool _dsconnectNotified = false;
        private void PrimitiveDisconnectNotify()
        {
            if (this._dsconnectNotified)
            {
                return;
            }
            this._dsconnectNotified = true;
            this._disconnectNotify(this);
        }

        private void DefaultReceiveDataNotify(NetTcpChannel netTcpChannel, int len)
        {

        }

        private void RevDataThreadMethod(ThreadExPara threadPara)
        {

            try
            {
                Socket client = (Socket)threadPara.Para;
                var unpackerPara = new TcpUnpackerPara();
                unpackerPara.LinkInfo = this._linkInfo;
                unpackerPara.Client = this._owner;
                byte[] buffer = new byte[this._bufferSize];
                unpackerPara.Buffer = buffer;

                BinaryReader reader;
                if (this._serverCertificate != null)
                {
                    //todo 理论代码先写好,ssl不好找机会验证
                    var sslStream = new SslStream(new MemoryStream(buffer));
                    sslStream.AuthenticateAsServer(this._serverCertificate, false, SslProtocols.Tls, true);
                    sslStream.ReadTimeout = 10000;
                    sslStream.WriteTimeout = 10000;
                    reader = new BinaryReader(sslStream);
                }
                else
                {
                    reader = new BinaryReader(new MemoryStream(buffer));
                }

                using (reader)
                {
                    unpackerPara.Reader = reader;

                    int preReceiveMaxSize;
                    if (buffer.Length >= NetHelper.MTU_MAX * 2)
                    {
                        //当缓存大小大于等于MTU_MAX2倍时,每次接收最大值为MTU_MAX
                        preReceiveMaxSize = NetHelper.MTU_MAX;
                    }
                    else
                    {
                        //否则为缓存大小的1/4
                        preReceiveMaxSize = buffer.Length / 4;
                    }

                    int preReceiveSize = preReceiveMaxSize, modReceiveSize;
                    int revLen, writeOffset = 0, readOffset = 0;
                    int totalDataLength = 0, unpackedDataLength, receiveEOFCount = 0;

                    while (true)
                    {
                        try
                        {
                            if (this._disposed || threadPara.Token.IsCancellationRequested)
                            {
                                break;
                            }

                            revLen = client.Receive(buffer, writeOffset, preReceiveSize, SocketFlags.None);
                            if (revLen <= 0)
                            {
                                //对方连接意外断开,接收长度为0
                                receiveEOFCount++;
                                if (receiveEOFCount >= 3)
                                {
                                    //当连续接收到长度为0的数据3次,确定对方断开,不再接收数据,由接收数据循环跳出后触发连接断开通知
                                    break;
                                }
                                else
                                {
                                    //注:即使是接收到数据长度等于0,也还是触发拆包,有些特殊场景下以发送数据长度为0的包来作边界处理,所以不能忽略长度为0的数据处理逻辑
                                    //continue;
                                }
                            }
                            else
                            {
                                writeOffset += revLen;
                                totalDataLength += revLen;
                                this._receiveDataNotify(this, revLen);//最后再来干这个

                                reader.BaseStream.Position = readOffset;
                                unpackerPara.Offset = readOffset;
                                unpackerPara.DataLength = totalDataLength;
                                unpackedDataLength = this._unpacker.UnPackge(unpackerPara);
                                readOffset += unpackedDataLength;
                                totalDataLength -= unpackedDataLength;

                                if (totalDataLength == 0)
                                {
                                    //总数据长度为0时,默认从0开始接收
                                    writeOffset = 0;
                                    readOffset = 0;
                                    preReceiveSize = preReceiveMaxSize;
                                }
                                else
                                {
                                    if (buffer.Length - writeOffset < preReceiveMaxSize)
                                    {
                                        //剩余接收数据空间不足,将未解析数据移动到buffer开始位置
                                        if (totalDataLength > 0)
                                        {
                                            Array.Copy(buffer, readOffset, buffer, 0, totalDataLength);
                                        }

                                        readOffset = 0;
                                        writeOffset = totalDataLength;
                                    }

                                    //重新计算准备接收数据长度
                                    modReceiveSize = buffer.Length - totalDataLength;
                                    if (preReceiveSize >= modReceiveSize)
                                    {
                                        preReceiveSize = modReceiveSize;
                                    }
                                    else
                                    {
                                        if (modReceiveSize >= preReceiveMaxSize)
                                        {
                                            preReceiveSize = preReceiveMaxSize;
                                        }
                                        else
                                        {
                                            preReceiveSize = modReceiveSize;
                                        }
                                    }
                                }
                            }
                        }
                        catch (ArgumentOutOfRangeException)
                        {
                            totalDataLength = 0;
                            reader.BaseStream.Position = 0L;
                            writeOffset = 0;
                        }
                        catch (SocketException sex)
                        {
                            if (!NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                            {
                                ZLoger.Error(sex, "接收数据发生未处理到的异常");
                            }
                            break;
                        }
                        catch (ObjectDisposedException)
                        {
                            break;
                        }
                        catch (ThreadAbortException)
                        {
                            break;
                        }
                        catch (Exception ex)
                        {
                            ZLoger.Error(ex, "接收数据异常");
                        }
                    }
                }
            }
            catch (ThreadAbortException)
            { }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }

            this.PrimitiveDisconnectNotify();
        }


        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        /// <param name="offset">数据中偏移位置</param>
        /// <param name="length">要发送的数据大小</param>
        /// <param name="socketFlags">SocketFlags</param>
        public void Send(byte[] buffer, int offset, int length, SocketFlags socketFlags)
        {
            int sendedLength;
            while (length > 0)
            {
                try
                {
                    sendedLength = this._client.Send(buffer, offset, length, socketFlags);
                    offset += sendedLength;
                    length -= sendedLength;
                }
                catch (ObjectDisposedException)
                {
                    break;
                }
                catch (SocketException sex)
                {
                    if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                    {
                        this.PrimitiveDisconnectNotify();
                    }

                    throw;
                }
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        public void Send(byte[] buffer)
        {
            int length = buffer.Length;
            int sendOffset = 0;
            while (sendOffset < length)
            {
                try
                {
                    sendOffset += this._client.Send(buffer, sendOffset, length - sendOffset, SocketFlags.None);
                }
                catch (ObjectDisposedException)
                {
                    break;
                }
                catch (SocketException sex)
                {
                    if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                    {
                        this.PrimitiveDisconnectNotify();
                    }

                    throw;
                }
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="stream">Stream</param>
        /// <param name="offset">偏移位置</param>
        /// <param name="length">数据长度</param>
        /// <param name="socketFlags">SocketFlags</param>
        public void Send(Stream stream, long offset, long length, SocketFlags socketFlags)
        {
            try
            {
                byte[] buffer = new byte[NetHelper.MTU_MAX];
                long mod;
                int count = buffer.Length;
                long endPosition = offset + length;
                stream.Seek(offset, SeekOrigin.Begin);
                while (stream.Position < endPosition)
                {
                    mod = endPosition - stream.Position;
                    if (mod < buffer.Length)
                    {
                        count = (int)mod;
                    }

                    stream.Read(buffer, 0, count);
                    this.Send(buffer, 0, buffer.Length, socketFlags);
                }
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.PrimitiveDisconnectNotify();
                }

                throw;
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        public void Send(string filePath)
        {
            try
            {
                this._client.SendFile(filePath);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.PrimitiveDisconnectNotify();
                }

                throw;
            }
        }


#if CORE
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        public void Send(Memory<byte> buffer)
        {
            try
            {
                this._client.Send(buffer.Span);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.PrimitiveDisconnectNotify();
                }

                throw;
            }
        }
#endif

        /// <summary>
        /// 对象释放标识[true:已释放;false:未释放]
        /// </summary>
        private bool _disposed = false;

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                if (this._disposed)
                {
                    return;
                }
                this._disposed = true;

                try
                {
                    this._socketRevDataThread.Stop();
                    this._socketRevDataThread.Dispose();
                }
                catch (ThreadAbortException)
                { }

                try
                {
                    this._client.Shutdown(SocketShutdown.Both);
                }
                catch (ObjectDisposedException)
                { }

                this._client.Dispose();
                this._unpacker.Dispose();

                //ZLoger.Warn("NetTcpChannel.Dispose");
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }
    }
}
